#!/usr/bin/env python
# coding=utf-8

# Writer : Katagiri
# Update : 2013/06/26
# Description : camfwClient class as camfwlib API.

# Standard library
import logging
import logging.handlers
import socket

# A part of camfwlib
import frame

# Constant definition
API_VERSION = 0.1

CRITICAL     = 50
ERROR         = 40
WARNING     = 30
INFO         = 20
DEBUG         = 10

class camfwClient():
    u"""
    このクラスは、camfw API としての全ての機能を提供します。
    アプリケーションは、本クラスのインスタンスを生成し、そのメソッドから各種機能を実行します。

    arguments :\n
        string host - camfwd のホスト名、あるいはIPアドレスを指定します。
        デフォルトは'localhost'です。\n
        int port - camfwd のポート番号を指定します。デフォルトは8000です。\n
    logfile - ログの出力先ファイルを指定します。無い場合、ログの出力先は標準出力となります。\n
    loglevel - ログ機能のレベルを指定します。
    レベルは5段階。本モジュールで定義している定数、CRITICAL,ERROR,WARNING,INFO,DEBUG により指定します。
    最高がCRITICAL、最低がDEBUGです。デフォルトはWARNINGです。
    set_loglevel(), log()メソッドにおいても同じ定数を使用します。
        
    """

    def __init__(self, host='localhost', port=8000, logfile=None, loglevel=WARNING):
        self.host = host
        self.port = port
        self._fb = frame.FrameBuilder()
        #self._logger = None

        self._logger = logging.getLogger('camfwlib')
        self._logger.propagate = False
        self._logger.setLevel(loglevel)

        if logfile == None:
            handler = logging.StreamHandler()
        else:
            handler = logging.handlers.RotatingFileHandler(filename=logfile,maxBytes=1024*1024, backupCount=5)

        formatter = logging.Formatter('%(asctime)s %(levelname)s @ %(name)s - %(message)s', '%a, %d %b %Y %H:%M:%S')
        handler.setFormatter(formatter)
        self._logger.addHandler(handler)

    def set_loglevel(self, level):
        u"""
        ログ機能のレベルを変更します。

        arguments :\n 
            int level - レベルを指定します。\n 
           return : なし
        """

        self._logger.setLevel(level)

    def log(self, level, message):
        u"""
        ログを出力します。

        arguments :\n
            int level - 出力するログのレベルを指定します。このレベルが、ログ機能に設定されているレベル以上の場合にのみ、出力が有効となります。\n
        camfwlib内部でも、ログ出力を行いますが、ログレベルを全てINFOとしています。\n
            string message - ログとして出力する任意の文字列。\n
        return : なし
        """

        self._logger.log(level, message)

    def _request(self, frame=None):
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.settimeout(3.0)
            s.connect((self.host, self.port))
            s.sendall(frame)
            receive_string = s.recv(16384)
            headers = self._fb.request_decode(receive_string[:35])
            response_length = int(headers['length'])
        
            while response_length > len(receive_string):
                rdata = s.recv(16384)
                receive_string = receive_string + rdata

        except socket.timeout, msg:
            s.close()
            s = None
            self._logger.info(msg)
            return False
        except socket.error, (errno, msg):
            s.close()
            s = None
            self._logger.error(str(error) + ' : ' + msg)
            return False

        s.shutdown(1)
        s.close()
        return receive_string

    def get_apiver(self):
        self._logger.info('get_apiver() called')
        return API_VERSION

    def set_daemon_addr(self, host, port):
        self._logger.info('set_daemon_addr() called')
        self.host = host
        self.port = port

    def _socket_connect(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(3.0)
        s.connect((self.host, self.port))
        return s

    def _socket_close(self, s):
        s.shutdown(1)
        s.close()

    def get_cameralist(self):
        u"""
        camfwd が認識しているアクティブなカメラのIDを取得します。\n

        arguments : なし\n
        return : 成功時はカメラのID(string)のリスト、失敗時はFalseが返る。
        """
        self._logger.info('get_cameralist() called')

        s = self._socket_connect()
        self._fb.send_request(s, frame.CAMERA_LIST)
        response_frame = self._fb.recv_response(s)
        self._socket_close(s)

        if 'param' not in response_frame:
            return False

        param = response_frame['param']
        if param['Success'] is False:
            self._logger.warning(param['Message'])
            return False

        return param['Response']

    def get_snapshot(self, cid, filepath):
        u"""
        指定したカメラのスナップショットを取得します。\n

        arguments :\n
            string cid - カメラID。\n
            string filepath - スナップショット画像を保存するファイルのパスを指定。\n
        return : 成功時はTrue、失敗時はFalseが返る。
        """
        self._logger.info('get_snapshot() called, cid : ' + cid)

        params        = {}
        params['cid'] = cid

        s = self._socket_connect()
        self._fb.send_request(s, frame.CAMERA_SNAPSHOT, params)
        response_frame = self._fb.recv_response(s)
        self._socket_close(s)

        if 'param' not in response_frame:
            return False

        param = response_frame['param']
        if param['Success'] is False:
            self._logger.warning(param['Message'])
            return False

        fp = open(filepath, "w")
        fp.write(param['Response'])
        fp.close()
        
        return True

    def restart_camera(self, cid):
        u"""
        ※  未実装\n
        カメラを再起動します。

        arguments :\n
        string cid - カメラID。\n
        return : 成功時はTrue、失敗時はFalseが返る。
        """

    def start_movie_streaming(self, cid):
        u"""
        カメラの動画ストリーミングを開始します。映像データは設定したストレージへ随時保存されます。

        arguments :\n
            string cid - カメラID。\n
        return : 成功時はTrue、失敗時はFalseが返る。
        """
        params        = {}
        params['cid'] = cid

        s = self._socket_connect()
        self._fb.send_request(s, frame.CAMERA_MOVIE_CAPTURE_START, params)
        response_frame = self._fb.recv_response(s)
        self._socket_close(s)

        if 'param' not in response_frame:
            return False

        param = response_frame['param']
        if param['Success'] is False:
            self._logger.warning(param['Message'])
            return False

        return True

    def stop_movie_streaming(self, cid):
        u"""
        カメラの動画ストリーミングを停止します。

        arguments :\n
            string cid - カメラID。\n
        return : 成功時はTrue、失敗時はFalseが返る。
        """
        params        = {}
        params['cid'] = cid

        s = self._socket_connect()
        self._fb.send_request(s, frame.CAMERA_MOVIE_CAPTURE_STOP, params)
        response_frame = self._fb.recv_response(s)
        self._socket_close(s)

        if 'param' not in response_frame:
            return False

        param = response_frame['param']
        if param['Success'] is False:
            self._logger.warning(param['Message'])
            return False

        return True

    def get_movie_imagelist(self, cid, query=None):
        u"""
        ストレージに蓄えた動画から切り出した静止画イメージリストを取得します\n

        arguments :\n
             string cid - カメラID。\n
                string query - クエリの指定\n
                    'first'                 : 一番古いフレーム\n
                    'last'                  : 一番新しいフレーム\n
                    'all'                   : 全てのフレーム\n
                    list(termfrom, termto) : 時間範囲指定\n
        return : イメージリスト
        """
        command       = None
        params        = {}
        params['cid'] = cid

        if query is None:
            command = frame.STORAGE_MOVIE_FRAME_ALL
        elif isinstance(query, str):
            if query == 'all':
                command = frame.STORAGE_MOVIE_FRAME_ALL
            elif query == 'first':
                command = frame.STORAGE_MOVIE_FRAME_FIRST
            elif query == 'last':
                command = frame.STORAGE_MOVIE_FRAME_LAST
        elif isinstance(query, list):
            command = frame.STORAGE_MOVIE_FRAME_RANGE
            params['range'] = query
            print params

        s = self._socket_connect()
        self._fb.send_request(s, command, params)
        response_frame = self._fb.recv_response(s)
        self._socket_close(s)

        if 'param' not in response_frame:
            return []

        param = response_frame['param']
        if param['Success'] is False:
            self._logger.warning(param['Message'])
            return []

        return param['Response']

    def get_movie_imagedata(self, framename):
        u"""
        ストレージに蓄えた静止画イメージデータを取得します\n

        arguments :\n
            string framename - get_imagelistで返されたリストの1要素\n
        return : イメージデータ
        """
        params        = {}
        params['framename'] = framename

        s = self._socket_connect()
        self._fb.send_request(s, frame.STORAGE_MOVIE_GETFRAME, params)
        response_frame = self._fb.recv_response(s)
        self._socket_close(s)

        if 'param' not in response_frame:
            return None

        param = response_frame['param']
        if param['Success'] is False:
            self._logger.warning(param['Message'])
            return None

        return param['Response']

    def start_audio_streaming(self, cid):
        u"""
        カメラの音声ストリーミングを開始します。音声データは設定したストレージへ随時保存されます。

        arguments :\n
            string cid - カメラID。\n
        return : 成功時はTrue、失敗時はFalseが返る。
        """
        params        = {}
        params['cid'] = cid

        s = self._socket_connect()
        self._fb.send_request(s, frame.CAMERA_AUDIO_CAPTURE_START, params)
        response_frame = self._fb.recv_response(s)
        self._socket_close(s)

        if 'param' not in response_frame:
            return False

        param = response_frame['param']
        if param['Success'] is False:
            self._logger.warning(param['Message'])
            return False

        return True

    def stop_audio_streaming(self, cid):
        u"""
        カメラの音声ストリーミングを停止します。

        arguments :\n
            string cid - カメラID。\n
        return : 成功時はTrue、失敗時はFalseが返る。
        """
        params        = {}
        params['cid'] = cid

        s = self._socket_connect()
        self._fb.send_request(s, frame.CAMERA_AUDIO_CAPTURE_STOP, params)
        response_frame = self._fb.recv_response(s)
        self._socket_close(s)

        if 'param' not in response_frame:
            return False

        param = response_frame['param']
        if param['Success'] is False:
            self._logger.warning(param['Message'])
            return False

        return True

    def get_audio_imagelist(self, cid, query=None):
        u"""
        ストレージに蓄えた音声イメージリストを取得します\n

        arguments :\n
             string cid - カメラID。\n
                string query - クエリの指定\n
                    'first'                 : 一番古いフレーム\n
                    'last'                  : 一番新しいフレーム\n
                    'all'                   : 全てのフレーム\n
                    list(termfrom, termto) : 時間範囲指定\n
        return : イメージリスト
        """
        command       = None
        params        = {}
        params['cid'] = cid

        if query is None:
            command = frame.STORAGE_AUDIO_FRAME_ALL
        elif isinstance(query, str):
            if query == 'all':
                command = frame.STORAGE_AUDIO_FRAME_ALL
            elif query == 'first':
                command = frame.STORAGE_AUDIO_FRAME_FIRST
            elif query == 'last':
                command = frame.STORAGE_AUDIO_FRAME_LAST
        elif isinstance(query, list):
            command = frame.STORAGE_AUDIO_FRAME_RANGE
            params['range'] = query
            print params

        s = self._socket_connect()
        self._fb.send_request(s, command, params)
        response_frame = self._fb.recv_response(s)
        self._socket_close(s)

        if 'param' not in response_frame:
            return []

        param = response_frame['param']
        if param['Success'] is False:
            self._logger.warning(param['Message'])
            return []

        return param['Response']

    def get_audio_imagedata(self, framename):
        u"""
        ストレージに蓄えた音声イメージデータを取得します\n

        arguments :\n
            string framename - get_imagelistで返されたリストの1要素\n
        return : イメージデータ
        """
        params        = {}
        params['framename'] = framename

        s = self._socket_connect()
        self._fb.send_request(s, frame.STORAGE_AUDIO_GETFRAME, params)
        response_frame = self._fb.recv_response(s)
        self._socket_close(s)

        if 'param' not in response_frame:
            return None

        param = response_frame['param']
        if param['Success'] is False:
            self._logger.warning(param['Message'])
            return None

        return param['Response']

    def set_framerate(self, cid, fps):
        u"""
        ※  未実装\n
        カメラのフレームレートを設定します。

        arguments :\n
            string cid - カメラID。\n
            int fps - フレームレート(frame per second)。\n
        return : 成功時はTrue、失敗時はFalseが返る。
        """

    def set_videoparam(self, type, value):
        u"""
        ※  未実装\n
        映像のパラメータを設定します。

        arguments :\n
            string type - パラメータの種類を指定、'brightness', 'contrast', 'saturation'。\n
            int value - 設定値。\n
        return : 成功時はTrue、失敗時はFalseが返る。
        """

    def set_lightfrequency(self, type):
        u"""
        ※  未実装\n
        環境光の周波数を指定します。

        arguments :\n
            string type - 周波数タイプを指定、'50hz', '60hz', 'outdoor'。\n
        return : 成功時はTrue、失敗時はFalseが返る。
        """
